home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / WASTE 1.2 Distribution / WASTE 1.2 / WELowLevelEditing.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-06-25  |  25.3 KB  |  928 lines  |  [TEXT/CWIE]

  1. /*
  2.  *    WELowLevelEditing.c
  3.  *
  4.  *    WASTE PROJECT
  5.  *  Low-Level Editing Routines
  6.  *
  7.  *  Copyright (c) 1993-1996 Marco Piovanelli
  8.  *    All Rights Reserved
  9.  *
  10.  *  C port by Dan Crevier
  11.  *
  12.  */
  13.  
  14.  
  15. #include "WASTEIntf.h"
  16.  
  17. pascal Boolean _WEIsWordRange(SInt32 rangeStart, SInt32 rangeEnd, WEHandle hWE)
  18. {
  19.     // _WEIsWordRange returns true if the specified range is a word range,
  20.     // i.e. if it would be possible to select it by double-clicking and (optionally) dragging.
  21.  
  22.     SInt32 wordStart, wordEnd;
  23.  
  24.     // determine if rangeStart is at the beginning of a word
  25.     WEFindWord(rangeStart, kLeadingEdge, &wordStart, &wordEnd, hWE);
  26.     if (rangeStart == wordStart)
  27.     {
  28.  
  29.         // determine if rangeEnd is at the end of a word
  30.         WEFindWord(rangeEnd, kTrailingEdge, &wordStart, &wordEnd, hWE);
  31.         return (rangeEnd == wordEnd);
  32.     }
  33.     return false;
  34. }
  35.  
  36. pascal Boolean _WEIsPunct(SInt32 offset, WEHandle hWE)
  37. {
  38.     SInt16 cType;
  39.  
  40.     cType = WECharType(offset, hWE);
  41.     if ((cType & smcTypeMask) == smCharPunct)
  42.     {
  43.         cType &= smcClassMask;
  44.         if ((cType == smPunctNormal) || (cType == smPunctBlank))
  45.             return true;
  46.     }
  47.     return false;
  48. }
  49.  
  50. pascal void _WEIntelligentCut(SInt32 *rangeStart, SInt32 *rangeEnd, WEHandle hWE)
  51. {
  52.  
  53.     // _WEIntelligentCut is called by other WASTE routines to determine the actual
  54.     // range to be deleted when weFIntCutAndPaste is enabled.
  55.     // On entry, rangeStart and rangeEnd specify the selection range visible to the user.
  56.     // On exit, rangeStart and rangeEnd specify the actual range to be removed.
  57.  
  58.     // do nothing if the intelligent cut-and-paste feature is disabled
  59.     if (!BTST((*hWE)->features, weFIntCutAndPaste))
  60.         return;
  61.  
  62.     // intelling cut-&-paste rules should be applied only to word ranges...
  63.     if (!_WEIsWordRange(*rangeStart, *rangeEnd, hWE))
  64.         return;
  65.  
  66.     // ...without punctuation characters at the beginning or end
  67.     if (_WEIsPunct(*rangeStart, hWE))
  68.         return;
  69.     if (_WEIsPunct(*rangeEnd - 1, hWE))
  70.         return;
  71.  
  72.     // if the character preceding the selection range is a space, discard it
  73.     if (WEGetChar(*rangeStart - 1, hWE) == kSpace)
  74.         (*rangeStart)--;
  75.     // else, if the character following the selection range is a space, discard it
  76.     else if (WEGetChar(*rangeEnd, hWE) == kSpace)
  77.         (*rangeEnd)++;
  78.  
  79. }
  80.  
  81. pascal SInt16 _WEIntelligentPaste(SInt32 rangeStart, SInt32 rangeEnd, WEHandle hWE)
  82. {
  83.     SInt16 retval;
  84.  
  85.     // _WEIntelligentPaste is called by other WASTE routines to determine whether
  86.     // an additional space character should be added (before or after) after inserting
  87.     // new text (usually from the Clipboard or from a drag).
  88.  
  89.     retval = weDontAddSpaces;
  90.  
  91.     // do nothing unless the intelligent cut-and-paste feature is enabled
  92.     if (!BTST((*hWE)->features, weFIntCutAndPaste))
  93.         return retval;
  94.  
  95.     // extra spaces will be added only if the pasted text looks like a word range,
  96.     // without punctuation characters at the beginning or at the end
  97.     if (_WEIsPunct(rangeStart, hWE))
  98.         return retval;
  99.     if (_WEIsPunct(rangeEnd - 1, hWE))
  100.         return retval;
  101.  
  102.     // if the character on the left of the pasted text is a punctuation character
  103.     // and the character on the right isn't,  add a space on the right, and vice versa
  104.     if (_WEIsPunct(rangeStart - 1, hWE))
  105.     {
  106.         if (!_WEIsPunct(rangeEnd, hWE))
  107.         {
  108.             retval = weAddSpaceOnRightSide;
  109.         }
  110.     }
  111.     else if (_WEIsPunct(rangeEnd, hWE))
  112.     {
  113.         retval = weAddSpaceOnLeftSide;
  114.     }
  115.  
  116.     return retval;
  117. }
  118.  
  119. pascal OSErr _WEInsertRun(SInt32 runIndex, SInt32 offset, SInt32 styleIndex, WEPtr pWE)
  120. {
  121.  
  122.     // Insert a new element in the style run array, at the specified runIndex position.
  123.     // The new element consists of the pair <offset, styleIndex>.
  124.  
  125.     RunArrayElement element;
  126.     OSErr err;
  127.  
  128.     // prepare the element record to be inserted in the array
  129.     element.runStart = offset;
  130.     element.styleIndex = styleIndex;
  131.  
  132.     // do the insertion
  133.     if ((err = _WEInsertBlock((Handle) pWE->hRuns, (const void *) &element, sizeof(element), (runIndex + 1) * sizeof(element))) != noErr)
  134.         return err;
  135.  
  136.     // increment style run count
  137.     pWE->nRuns++;
  138.  
  139.     // increment the reference count field of the style table element
  140.     // referenced by the newly inserted style run
  141.     (*pWE->hStyles)[styleIndex].refCount++;
  142.  
  143.     return noErr;
  144. }
  145.  
  146. pascal void _WERemoveRun(SInt32 runIndex, WEPtr pWE)
  147. {
  148.     StyleTableElementPtr pStyle;
  149.  
  150.     // get a pointer to the style table element referenced by the style run
  151.     pStyle = *pWE->hStyles + (*pWE->hRuns)[runIndex].styleIndex;
  152.  
  153.     // decrement the reference count field of the style table element
  154.     // referenced by the style run to be removed
  155.     pStyle->refCount--;
  156.  
  157. #if WASTE_OBJECTS
  158.     // dispose of the embedded object (if any)
  159.     if (pStyle->info.runStyle.tsObject != nil)
  160.         _WEFreeObject(pStyle->info.runStyle.tsObject);
  161. #endif
  162.  
  163.     // remove a "slot" from the run array
  164.     _WERemoveBlock((Handle) pWE->hRuns, sizeof(RunArrayElement), runIndex * sizeof(RunArrayElement));
  165.  
  166.     // decrement style run count
  167.     pWE->nRuns--;
  168.  
  169. }
  170.  
  171. pascal void _WEChangeRun(SInt32 runIndex, SInt32 newStyleIndex, Boolean keepOld, WEPtr pWE)
  172. {
  173.     // change the styleIndex field of the specified element of the style run array
  174.  
  175.     SInt32 oldStyleIndex;
  176.     StyleTableElementPtr oldStyle, newStyle;
  177.  
  178.     // do the change
  179.     oldStyleIndex = (*pWE->hRuns)[runIndex].styleIndex;
  180.     (*pWE->hRuns)[runIndex].styleIndex = newStyleIndex;
  181.  
  182.     // get pointers to old and new style table elements
  183.     oldStyle = *pWE->hStyles + oldStyleIndex;
  184.     newStyle = *pWE->hStyles + newStyleIndex;
  185.  
  186.     // increment the reference count field of the new style table element
  187.     newStyle->refCount++;
  188.  
  189.     // decrement the reference count field of the old style table element
  190.     oldStyle->refCount--;
  191.  
  192. #if WASTE_OBJECTS
  193.     // dispose of the embedded object (if any) unless it is again referenced in the new style
  194.     if (!keepOld)
  195.     {
  196.         WEObjectDescHandle oldObject = oldStyle->info.runStyle.tsObject;
  197.  
  198.         if ((oldObject != nil) && (oldObject != newStyle->info.runStyle.tsObject))
  199.             _WEFreeObject(oldObject);
  200.     }
  201. #endif
  202.  
  203. }
  204.  
  205. pascal OSErr _WENewStyle(const WETextStyle *ts, SInt32 *styleIndex, WEPtr pWE)
  206. {
  207.     // given the specified WETextStyle record, find the corresponding entry
  208.     // in the style table (create a new entry if necessary), and return its index
  209.  
  210.     StyleTableElementPtr pElement;
  211.     StyleTableElement element;
  212.     SInt32 index;
  213.     SInt32 unusedIndex;
  214.     OSErr err;
  215.  
  216.     // see if the given style already exists in the style table
  217.     // while scanning the table, also remember the position of the first unused style, if any
  218.     unusedIndex = -1;
  219.     pElement = *pWE->hStyles;
  220.     for ( index = 0 ; index < pWE->nStyles ; index++ )
  221.     {
  222.         // check for entries which aren't referenced and can be recycled
  223.         if (pElement->refCount == 0)
  224.             unusedIndex = index;
  225.  
  226.         // perform a bitwise comparison between the current element and the specified style
  227.         // (actually, we ignore metrics information)
  228.         else if (_WEBlockCmp(&pElement->info.runStyle, ts, sizeof(WETextStyle)))
  229.         {
  230.             // found: style already present
  231.             *styleIndex = index;
  232.             return noErr;
  233.         }
  234.         pElement++;
  235.     } // for
  236.  
  237.     // the specified style doesn't exist in the style table
  238.     // since this is a new entry, we have to calculate font metrics information
  239.     element.info.runStyle = *ts;
  240.     _WEFillFontInfo(pWE->port, &element.info);
  241. /*
  242.     // set the high bit of tsFlags if attributes specify a right-to-left run
  243.     if (BTST(pWE->flags, weFBidirectional))
  244.     {
  245.         if (GetScriptVariable(FontToScript(element.info.runStyle.tsFont), smScriptRight) != 0)
  246.         {
  247.             BSET(element.info.runStyle.tsFlags, tsRightToLeft);
  248.         }
  249.     }
  250. */
  251.     // see if we can recycle an unused entry
  252.     if (unusedIndex >= 0)
  253.     {
  254.         index = unusedIndex;
  255.         (*pWE->hStyles)[index].info = element.info;
  256.     }
  257.     else
  258.     {
  259.         // no reusable entry: we have to append a new element to the table
  260.         element.refCount = 0;
  261.         if ((err = _WEInsertBlock((Handle) pWE->hStyles, &element, sizeof(element), index * sizeof(element))) != noErr)
  262.             return err;
  263.  
  264.         // update style count in the WE record
  265.         pWE->nStyles++;
  266.     }
  267.  
  268.     // return the index to the new element
  269.     *styleIndex = index;
  270.     return noErr;
  271. }
  272.  
  273. pascal OSErr _WERedraw(SInt32 rangeStart, SInt32 rangeEnd, WEHandle hWE)
  274. {
  275.     WEPtr pWE = *hWE;        // assume WE record is already locked
  276.     LineArrayPtr pLines;
  277.     SInt32 startLine, endLine;
  278.     SInt32 oldTextHeight, newTextHeight;
  279.     LongRect r;
  280.     Rect viewRect, updateRect;
  281.     RgnHandle saveClip;
  282.     GrafPtr savePort;
  283.     OSErr err;
  284. #if WASTE_REDRAW_SPEED
  285.     LongRect scrollRect;
  286.     RgnHandle updateRgn,
  287.               utilRgn;
  288. #endif
  289.  
  290.     // do nothing if recalculation has been inhibited
  291.     if (!BTST(pWE->features, weFInhibitRecal))
  292.     {
  293.         // hide the caret
  294. #if WASTE_REDRAW_SPEED
  295.         BCLR(pWE->flags, weFCaretVisible);
  296. #else
  297.         if (BTST(pWE->flags, weFCaretVisible))
  298.         {
  299.             _WEBlinkCaret(hWE);
  300.         }
  301. #endif
  302.  
  303.         // remember total text height
  304.         oldTextHeight = pWE->destRect.bottom - pWE->destRect.top;
  305.  
  306.         // find line range affected by modification
  307.         startLine = WEOffsetToLine(rangeStart, hWE);
  308.         endLine = WEOffsetToLine(rangeEnd, hWE);
  309.  
  310.         // recalculate line breaks starting from startLine
  311.         if ((err = _WERecalBreaks(&startLine, &endLine, hWE)) != noErr)
  312.         {
  313.             goto cleanup;
  314.         }
  315.  
  316.         // recalculate slops
  317.         _WERecalSlops(startLine, endLine, hWE);
  318.  
  319.         // do nothing if redrawing has been inhibited
  320.         if (!BTST(pWE->features, weFInhibitRedraw))
  321.         {
  322.  
  323.             // calculate new total text height
  324.             newTextHeight = pWE->destRect.bottom - pWE->destRect.top;
  325.  
  326.             // calculate the rectangle to redraw (in long coordinates)
  327.             r.left = -SHRT_MAX;
  328.             r.right = SHRT_MAX;
  329.             pLines = *pWE->hLines;
  330.             r.top = pLines[startLine].lineOrigin;
  331.  
  332. #if WASTE_REDRAW_SPEED
  333.             // if total text height hasn't changed, it's enough to redraw lines up to endLine
  334.             // otherwise we must redraw all lines from startLine on
  335.  
  336.             if (endLine < pWE->nLines - 1)
  337.                  r.bottom = pLines[endLine + 1].lineOrigin;
  338.             else
  339.                 r.bottom = newTextHeight;
  340.             WEOffsetLongRect(&r, 0, pWE->destRect.top);
  341.  
  342.             if (newTextHeight == oldTextHeight)
  343.                 WELongRectToRect(&r, &updateRect);
  344.             else
  345.             {
  346.                 /*      Instead of scrolling the lines below the deleted text up by redrawing them,
  347.                  *      use scroll bits to move the displayed text up.
  348.                  */
  349.  
  350.                 scrollRect = pWE->viewRect;
  351.                 if (newTextHeight > oldTextHeight)
  352.                     scrollRect.top = pLines[startLine + 1].lineOrigin + pWE->destRect.top;
  353.                 else
  354.                     scrollRect.top = pLines[startLine].lineOrigin + pWE->destRect.top;
  355.                 WELongRectToRect(&scrollRect, &updateRect);
  356.                 updateRgn = NewRgn();
  357.                 ScrollRect(&updateRect, 0, newTextHeight - oldTextHeight, updateRgn);
  358.  
  359.                 //      Redraw the exposed region (caused by a scroll up)
  360.  
  361.                 WELongRectToRect(&r, &updateRect);
  362.                 utilRgn = NewRgn();
  363.                 RectRgn(utilRgn, &updateRect);
  364.                 DiffRgn(updateRgn, utilRgn, updateRgn);
  365.                 DisposeRgn(utilRgn);
  366.                 WEUpdate(updateRgn, hWE);
  367.                 DisposeRgn(updateRgn);
  368.             }
  369. #else
  370.             // if total text height hasn't changed, it's enough to redraw lines up to endLine
  371.             // otherwise we must redraw all lines from startLine on
  372.             if ((newTextHeight == oldTextHeight) && (endLine < pWE->nLines - 1))
  373.             {
  374.                 r.bottom = pLines[endLine + 1].lineOrigin;
  375.             }
  376.             else if (newTextHeight < oldTextHeight)
  377.             {
  378.                 r.bottom = oldTextHeight;
  379.             }
  380.             else
  381.             {
  382.                 r.bottom = newTextHeight;
  383.             }
  384.  
  385.             WEOffsetLongRect(&r, 0, pWE->destRect.top);
  386.  
  387.             // calculate the intersection between this rectangle and the view rectangle
  388.             WELongRectToRect(&r, &updateRect);
  389. #endif
  390.             WELongRectToRect(&pWE->viewRect, &viewRect);
  391.  
  392.             if (SectRect(&updateRect, &viewRect, &updateRect))
  393.             {
  394.                 // set up the port and the clip region
  395.                 GetPort(&savePort);
  396.                 SetPort(pWE->port);
  397.  
  398.                 // set the clip region to updateRect
  399.                 saveClip = NewRgn();
  400.                 GetClip(saveClip);
  401.                 ClipRect(&updateRect);
  402.  
  403.                 // we only really need to redraw the visible lines
  404.                 startLine = _WEPixelToLine(updateRect.top - pWE->destRect.top, hWE);
  405.                 endLine = _WEPixelToLine(updateRect.bottom - pWE->destRect.top - 1, hWE);
  406.  
  407.                 // redraw the lines (pass true in the doErase parameter)
  408.                 _WEDrawLines(startLine, endLine, true, hWE);
  409.  
  410.                 // erase the portion of the update rectangle below the last line (if any)
  411.                 pLines = *pWE->hLines;
  412.                 updateRect.top = pWE->destRect.top + pLines[endLine + 1].lineOrigin;
  413.                 if (updateRect.top < updateRect.bottom)
  414.                 {
  415.                     EraseRect(&updateRect);
  416.                 }
  417.  
  418.                 // restore the clip region
  419.                 SetClip(saveClip);
  420.                 DisposeRgn(saveClip);
  421.  
  422.                 // restore the port
  423.                 SetPort(savePort);
  424.  
  425.                 // redraw the caret or the selection range
  426.                 if (pWE->selStart < pWE->selEnd)
  427.                 {
  428.                     _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  429.                 }
  430.                 else
  431.                 {
  432.                     _WEBlinkCaret(hWE);
  433.                 }
  434.             } // if SectRect
  435.  
  436.             // scroll the selection range into view
  437.             WESelView(hWE);
  438.  
  439.         } // if redraw not inhibited
  440.     } // if recal not inhibited
  441.  
  442.     // clear result code
  443.     err = noErr;
  444.  
  445. cleanup:
  446.     // return result code
  447.     return err;
  448. }
  449.  
  450. pascal OSErr WECalText(WEHandle hWE)
  451. {
  452.     Boolean saveWELock;
  453.     OSErr err;
  454.  
  455.     // lock WE record
  456.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  457.  
  458. #if WASTE_WECALTEXT_DOES_REDRAW
  459.  
  460.     // recalculate line breaks & slops and redraw the text
  461.     err = _WERedraw(0, LONG_MAX, hWE);
  462.  
  463. #else
  464.  
  465.     {
  466.         SInt32 startLine = 0;
  467.         SInt32 endLine = LONG_MAX;
  468.  
  469.         // recalculate line breaks & slops without redrawing anything
  470.         if ((err = _WERecalBreaks(&startLine, &endLine, hWE)) == noErr)
  471.             _WERecalSlops(startLine, endLine, hWE);
  472.     }
  473.  
  474. #endif
  475.  
  476.     // unlock the WE record
  477.     _WESetHandleLock((Handle) hWE, saveWELock);
  478.  
  479.     // return result code
  480.     return err;
  481. }
  482.  
  483. pascal OSErr _WESetStyleRange(SInt32 rangeStart, SInt32 rangeEnd, WEStyleMode mode, const WETextStyle *ts, WEHandle hWE)
  484. {
  485.     // alter the style attributes of the specified text range according to ts and mode
  486.  
  487.     WEPtr pWE = *hWE;                    // assume WE record is already locked
  488.     RunArrayHandle hRuns = pWE->hRuns;
  489.     SInt32 offset;
  490.     SInt32 runIndex;
  491.     SInt32 oldStyleIndex, newStyleIndex;
  492.     WERunInfo runInfo;
  493.     Style continuousStyles;
  494.     OSErr err;
  495.  
  496.     WEASSERT(rangeStart < rangeEnd, "\pBad style range");
  497.  
  498.     // if mode contains weDoToggleFace, we need to determine which Quickdraw styles
  499.     // are continuous over the specified text range: those styles must be turned off
  500.     if (BTST(mode, kModeToggleFace))
  501.     {
  502.         WEStyleMode temp = weDoFace;
  503.         _WEContinuousStyleRange(rangeStart, rangeEnd, &temp, &runInfo.runAttrs.runStyle, hWE);
  504.         continuousStyles = runInfo.runAttrs.runStyle.tsFace;
  505.     }
  506.     else
  507.     {
  508.         continuousStyles = normal;
  509.     }
  510.  
  511.     // find the index to the first style run in the specified range
  512.     offset = rangeStart;
  513.     runIndex = _WEOffsetToRun(offset, hWE);
  514.  
  515.     // run thru all the style runs that encompass the selection range
  516.     do
  517.     {
  518.         // find style index for this run and retrieve corresponding style attributes
  519.         oldStyleIndex = (*hRuns)[runIndex].styleIndex;
  520.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  521.  
  522.         // _WEGetIndStyle returns textLength + 1 in runInfo.runEnd for the last style run:
  523.         // correct this anomaly (which is useful for other purposes, anyway)
  524.         if (runInfo.runEnd > pWE->textLength)
  525.         {
  526.             runInfo.runEnd = pWE->textLength;
  527.         }
  528.  
  529.         // apply changes to existing style attributes as requested
  530.         _WECopyStyle(ts, &runInfo.runAttrs.runStyle, continuousStyles, mode);
  531.  
  532.         // get a style index for the new text style
  533.         if ((err = _WENewStyle(&runInfo.runAttrs.runStyle, &newStyleIndex, pWE)) != noErr)
  534.             goto cleanup;
  535.  
  536.         // if offset falls on a style boundary and this style run has become identical
  537.         // to the previous one, merge the two runs together
  538.         if ((offset == runInfo.runStart) && (runIndex > 0) &&
  539.             ((*hRuns)[runIndex - 1].styleIndex == newStyleIndex))
  540.         {
  541.             _WERemoveRun(runIndex, pWE);
  542.             runIndex--;
  543.         }
  544.  
  545.         // style index changed?
  546.         if (oldStyleIndex != newStyleIndex)
  547.         {
  548.             // if offset is in the middle of a style run, insert a new style run in the run array
  549.             if (offset > runInfo.runStart)
  550.             {
  551.                 if ((err = _WEInsertRun(runIndex, offset, newStyleIndex, pWE)) != noErr)
  552.                 {
  553.                     goto cleanup;
  554.                 }
  555.                 runIndex++;
  556.             }
  557.             else
  558.             {
  559.                 // otherwise just change the styleIndex field of the current style run element
  560.                 _WEChangeRun(runIndex, newStyleIndex, (rangeEnd < runInfo.runEnd), pWE);
  561.             }
  562.  
  563.             // if specified range ends in the middle of a style run, insert yet another element
  564.             if (rangeEnd < runInfo.runEnd)
  565.             {
  566.                 if ((err = _WEInsertRun(runIndex, rangeEnd, oldStyleIndex, pWE)) != noErr)
  567.                 {
  568.                     goto cleanup;
  569.                 }
  570.             }
  571.         } // if oldStyle != newStyle
  572.  
  573.         // go to next style run
  574.         runIndex++;
  575.         offset = runInfo.runEnd;
  576.  
  577.     } while (offset < rangeEnd);
  578.  
  579.     // if the last style run ends exactly at the end of the specified range,
  580.     // see if we can merge it with the following style run
  581.     if ((offset == rangeEnd) && (runIndex < pWE->nRuns) &&
  582.         ((*hRuns)[runIndex].styleIndex == newStyleIndex))
  583.     {
  584.         _WERemoveRun(runIndex, pWE);
  585.     }
  586.  
  587.     // clear result code
  588.     err = noErr;
  589.  
  590. cleanup:
  591.     // return result code
  592.     return err;
  593. }
  594.  
  595. pascal OSErr _WEApplyStyleScrap(SInt32 rangeStart, SInt32 rangeEnd, StScrpHandle styleScrap, WEHandle hWE)
  596. {
  597.     // apply the given style scrap to the specified text range
  598.  
  599.     WEPtr pWE = *hWE;    // assume WE record is already locked
  600.     TEStyleScrapElement *pElement;
  601.     SInt32 runStart, runEnd;
  602.     SInt16 index, lastElement;
  603.     WETextStyle ts;
  604.     OSErr err;
  605.  
  606.     // loop through each element of the style scrap
  607.     lastElement = (*styleScrap)->scrpNStyles - 1;
  608.     for(index = 0; index <= lastElement; index++)
  609.     {
  610.         // get a pointer to the current scrap element
  611.         pElement = (TEStyleScrapElement *) ((*styleScrap)->scrpStyleTab + index);
  612.  
  613.         // calculate text run to which this element is to be applied
  614.         runStart = rangeStart + pElement->scrpStartChar;
  615.         if (index < lastElement)
  616.         {
  617.             runEnd = rangeStart + pElement[1].scrpStartChar;
  618.         }
  619.         else
  620.         {
  621.             runEnd = rangeEnd;
  622.         }
  623.  
  624.         // perform some range checking
  625.         if (runEnd > rangeEnd)
  626.         {
  627.             runEnd = rangeEnd;
  628.         }
  629.         if (runStart >= runEnd)
  630.         {
  631.             continue;
  632.         }
  633.  
  634.         // copy style to a local variable in case memory moves
  635.         * (TextStyle *) &ts = pElement->scrpTEAttrs.runTEStyle;
  636.  
  637.         // apply the specified style to the range
  638.         if ((err = _WESetStyleRange(runStart, runEnd, weDoAll + weDoReplaceFace, &ts, hWE)) != noErr)
  639.         {
  640.             return err;
  641.         }
  642.     }
  643.     return noErr;
  644. }
  645.  
  646. #if WASTE_OBJECTS
  647.  
  648. pascal OSErr _WEApplySoup(SInt32 offset, Handle hSoup, WEHandle hWE)
  649. {
  650.     WESoup soup;
  651.     UInt32 pSoup, pSoupEnd;
  652.     WETextStyle ts;
  653.     Handle hObjectData;
  654.     SInt32 objectOffset;
  655.     Boolean saveWELock;
  656.     OSErr err;
  657.  
  658.     BLOCK_CLR(ts);
  659.     hObjectData = nil;
  660.  
  661.     // lock the WE record
  662.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  663.  
  664.     // lock the soup in high heap
  665.     HLockHi(hSoup);
  666.     pSoup = (UInt32) *hSoup;
  667.     pSoupEnd = pSoup + GetHandleSize(hSoup);
  668.  
  669.     // loop through each object descriptor in the soup
  670.     while (pSoup < pSoupEnd)
  671.     {
  672.         // Object descriptors may be aligned to odd addresses (duh!)
  673.         // this isn't a problem on 68020+ and PPC CPUs, but causes
  674.         // a fatal address error on the 68000.  To avoid this, we
  675.         // copy the descriptor to the stack with BlockMoveData()
  676.         // before trying to access its fields.
  677.         BlockMoveData((const void *) pSoup, &soup, sizeof(soup));
  678.  
  679.         // if soupDataSize is negative, this soup is a special type that we won't handle here
  680.         if (soup.soupDataSize < 0)
  681.             continue;
  682.  
  683.         // create a new relocatable block the hold the object data
  684.         if ((err = _WEAllocate(soup.soupDataSize, kAllocTemp, &hObjectData)) != noErr)
  685.             goto cleanup;
  686.  
  687.         // copy the object data to this block
  688.         BlockMoveData((const void *) (pSoup + sizeof(soup)), *hObjectData, soup.soupDataSize);
  689.  
  690.         // create a new object out of the tagged data
  691.         if ((err = _WENewObject(soup.soupType, hObjectData, hWE, &ts.tsObject)) != noErr)
  692.             goto cleanup;
  693.  
  694.         // if there was no new handler for this object, use the object size stored in the soup
  695.         if ((*ts.tsObject)->objectTable == nil)
  696.             (*ts.tsObject)->objectSize = soup.soupSize;
  697.  
  698.         // record a reference to the object descriptor in the style table
  699.         objectOffset = soup.soupOffset + offset;
  700.         err = _WESetStyleRange(objectOffset, objectOffset + 1, weDoObject, &ts, hWE);
  701.         hObjectData = nil;
  702.         ts.tsObject = nil;
  703.         if (err != noErr)
  704.             goto cleanup;
  705.  
  706.         // advance soup pointer
  707.         pSoup += sizeof(soup) + soup.soupDataSize;
  708.  
  709.     } // while
  710.  
  711.     // clear result code
  712.     err = noErr;
  713.  
  714. cleanup:
  715.     // clean up
  716.     HUnlock(hSoup);
  717.     _WEForgetHandle((Handle *) &ts.tsObject);
  718.     _WEForgetHandle(&hObjectData);
  719.  
  720.     // unlock the WE record
  721.     _WESetHandleLock((Handle) hWE, saveWELock);
  722.  
  723.     // return result code
  724.     return err;
  725.  
  726. }
  727.  
  728. #endif    // WASTE_OBJECTS
  729.  
  730. pascal void _WEBumpRunStart(SInt32 runIndex, SInt32 deltaRunStart, WEPtr pWE)
  731. {
  732.     // add deltaLineStart to the lineStart field of all line records
  733.     // starting from lineIndex
  734.  
  735.     RunArrayElementPtr pRun = *pWE->hRuns + runIndex;
  736.     SInt32 nRuns = pWE->nRuns;
  737.  
  738.     // loop through the style run array adjusting the runStart fields
  739.     for ( ; runIndex <= nRuns; runIndex++ )
  740.     {
  741.         pRun->runStart += deltaRunStart;
  742.         pRun++;
  743.     }
  744. }
  745.  
  746. pascal void _WERemoveRunRange(SInt32 rangeStart, SInt32 rangeEnd, WEHandle hWE)
  747. {
  748.     // the range of text between rangeStart and rangeEnd is being deleted
  749.     // update the style run array (and the style table) accordingly
  750.  
  751.     WEPtr pWE = *hWE;    // assume WE record is already locked
  752.     RunArrayElementPtr pRun;
  753.     SInt32 startRun, endRun;
  754.  
  755.     // find the index to the first and last style runs in the specified range
  756.     startRun = _WEOffsetToRun(rangeStart, hWE);
  757.     endRun = _WEOffsetToRun(rangeEnd, hWE) - 1;
  758.  
  759.     // remove all style runs between startRun and endRun
  760.     for ( ; endRun > startRun; endRun-- )
  761.         _WERemoveRun(endRun, pWE);
  762.  
  763.     // move back all subsequent style runs
  764.     _WEBumpRunStart(startRun + 1, rangeStart - rangeEnd, pWE);
  765.  
  766.     if ((endRun == startRun) && (endRun < pWE->nRuns - 1))
  767.     {
  768.         pRun = *pWE->hRuns + endRun;
  769.         pRun[1].runStart = rangeStart;
  770.     }
  771.  
  772.     // remove the first style run if is has become zero length
  773.     pRun = *pWE->hRuns + startRun;
  774.     if (pRun[0].runStart == pRun[1].runStart)
  775.     {
  776.         _WERemoveRun(startRun, pWE);
  777.         startRun--;
  778.     }
  779.  
  780.     // merge the first and last runs if they have the same style index
  781.     if (startRun >= 0)
  782.     {
  783.         pRun = *pWE->hRuns + startRun;
  784.         if (pRun[0].styleIndex == pRun[1].styleIndex)
  785.         {
  786.             _WERemoveRun(startRun + 1, pWE);
  787.         }
  788.     }
  789. }
  790.  
  791. pascal void _WEBumpLineStart(SInt32 lineIndex, SInt32 deltaLineStart, WEPtr pWE)
  792. {
  793.     // add deltaLineStart to the lineStart field of all line records
  794.     // starting from lineIndex
  795.  
  796.     LineRec *pLine = *pWE->hLines + lineIndex;
  797.     SInt32 nLines = pWE->nLines;
  798.  
  799.     // loop through the line array adjusting the lineStart fields
  800.     for ( ; lineIndex <= nLines; lineIndex++ )
  801.     {
  802.         pLine->lineStart += deltaLineStart;
  803.         pLine++;
  804.     }
  805. }
  806.  
  807. pascal void _WERemoveLineRange(SInt32 rangeStart, SInt32 rangeEnd, WEHandle hWE)
  808. {
  809.     // the range of text between rangeStart and rangeEnd is being deleted
  810.     // update the line array accordingly
  811.  
  812.     WEPtr pWE = *hWE;    // assume WE record is already locked
  813.     SInt32 startLine, nLines;
  814.  
  815.     // remove all line records between rangeStart and rangeEnd
  816.     startLine = WEOffsetToLine(rangeStart, hWE) + 1;
  817.     nLines = WEOffsetToLine(rangeEnd, hWE) - startLine + 1;
  818.  
  819.     _WERemoveBlock((Handle) pWE->hLines, nLines * sizeof(LineRec), startLine * sizeof(LineRec));
  820.     pWE->nLines -= nLines;
  821.  
  822.     // update the lineStart field of all the line records that follow
  823.     _WEBumpLineStart(startLine, rangeStart - rangeEnd, pWE);
  824. }
  825.  
  826. pascal OSErr _WEDeleteRange(SInt32 rangeStart, SInt32 rangeEnd, WEHandle hWE)
  827. {
  828.     // used internally to delete a text range
  829.     WEPtr pWE = *hWE;    // assume WE record is already locked
  830.     WERunInfo runInfo;
  831.     OSErr err;
  832.  
  833.     if (rangeEnd > pWE->textLength)
  834.         rangeEnd = pWE->textLength;
  835.  
  836.     // do nothing if the specified range is empty
  837.     if (rangeStart == rangeEnd)
  838.         goto cleanup1;
  839.  
  840.     // save the first style in the specified range in nullStyle
  841.     WEGetRunInfo(rangeStart, &runInfo, hWE);
  842.     pWE->nullStyle = runInfo.runAttrs;
  843.     BSET(pWE->flags, weFUseNullStyle);
  844.  
  845. #if WASTE_OBJECTS
  846.     // special case: if we're deleting up to the end of the text, see whether
  847.     // there's an embedded object at the very end and remove it
  848.     if (rangeEnd == pWE->textLength)
  849.     {
  850.         WEGetRunInfo(rangeEnd - 1, &runInfo, hWE);
  851.         if (runInfo.runAttrs.runStyle.tsObject != nil)
  852.         {
  853.             runInfo.runAttrs.runStyle.tsObject = nil;
  854.             if ((err = _WESetStyleRange(rangeEnd - 1, rangeEnd, weDoObject, &runInfo.runAttrs.runStyle, hWE)) != noErr)
  855.                 goto cleanup2;
  856.         }
  857.     }
  858. #endif
  859.  
  860.     // remove all line records between rangeStart and rangeEnd
  861.     _WERemoveLineRange(rangeStart, rangeEnd, hWE);
  862.  
  863.     // remove all style runs between rangeStart and rangeEnd
  864.     _WERemoveRunRange(rangeStart, rangeEnd, hWE);
  865.  
  866.     // remove the text
  867.     _WERemoveBlock(pWE->hText, rangeEnd - rangeStart, rangeStart);
  868.  
  869.     // update textLength field
  870.     pWE->textLength -= (rangeEnd - rangeStart);
  871.  
  872.     // we modified the text, so the anchor range (if any) is no longer valid
  873.     pWE->clickCount = 0;
  874.  
  875. cleanup1:
  876.     // clear result code
  877.     err = noErr;
  878.  
  879. cleanup2:
  880.     // return result code
  881.     return err;
  882. }
  883.  
  884. pascal OSErr _WEInsertText(SInt32 offset, Ptr textPtr, SInt32 textLength, WEHandle hWE)
  885. {
  886.     WEPtr pWE = *hWE;    // assume WE record is already locked
  887.     WEStyleMode mode;
  888.     OSErr err;
  889.  
  890.     // do nothing if textLength is zero or negative
  891.     if (textLength <= 0)
  892.         return noErr;
  893.  
  894.     // insert the text
  895.     if ((err = _WEInsertBlock(pWE->hText, textPtr, textLength, offset)) != noErr)
  896.         return err;
  897.  
  898.     // update the lineStart fields of all lines following the insertion point
  899.     _WEBumpLineStart(WEOffsetToLine(offset, hWE) + 1, textLength, pWE);
  900.  
  901.     // update the runStart fields of all style runs following the insertion point
  902.     _WEBumpRunStart(_WEOffsetToRun(offset - 1, hWE) + 1, textLength, pWE);
  903.  
  904.     // update the textLength field
  905.     pWE->textLength += textLength;
  906.  
  907.     // we modified the text, so the anchor range (if any) is no longer valid
  908.     pWE->clickCount = 0;
  909.  
  910.     // make sure the newly inserted text doesn't reference any embedded object
  911. #if WASTE_OBJECTS
  912.     pWE->nullStyle.runStyle.tsObject = nil;
  913. #endif
  914.     mode = weDoObject;
  915.  
  916.     // if there is a valid null style, apply it to the newly inserted text
  917.     if (BTST(pWE->flags, weFUseNullStyle))
  918.     {
  919.         mode += (weDoAll + weDoReplaceFace);
  920.     }
  921.  
  922.     if ((err = _WESetStyleRange(offset, offset + textLength, mode, &pWE->nullStyle.runStyle, hWE)) != noErr)
  923.         return err;
  924.  
  925.     return noErr;
  926.  
  927. }
  928.